Scientific Reports https://www.nature.com/srep/¶Scientific Reports - журнал с откртытым доступом, который публикует оригинальные исследования из различных областей естественных и клинических наук, который предлагает своим авторам высоко уважаемое место для своих исследовваний. Scientific Reports - одиннадцатый по цитируемости журнал в мире, с показателями больше чем 300 000 цитат в 2018 году. Журнал получает широкое внимание в политике и средствах массовой информации.
Цель данного исследования подразумевает получение промежуточных знаний, необходимых для будущего решения более сложных задач, связанных с областью науки о данных в сфере научного знания. Scientific Reports на ряду с другими источниками науныных данных представляет интерес для понимания общей тенденции происходящей в мировой науке. Изучение тематических состввляющих, периодов публикации, авторского состава и других особенностей авторов и их работ, может послужить более правильному пониманию области и вырабатать эффективные шаги для последующего решания различных задача связанных с обработкой естественного языка применительно к научным публикациям.
import ast
import calendar
import re
from datetime import datetime
from glob import glob
import cufflinks as cf
import dateparser
import folium
import nltk
import numpy as np
import pandas as pd
import plotly
import plotly.figure_factory as ff
import plotly.graph_objs as go
import plotly.tools as tls
from folium.plugins import MarkerCluster
from IPython.display import HTML, Markdown, clear_output, display
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from plotly.offline import init_notebook_mode, iplot
from py_linq import Enumerable
from tqdm import notebook, tqdm
from sklearn.feature_extraction.text import CountVectorizer
init_notebook_mode(connected=True)
cf.go_offline(connected=True)
cf.set_config_file(world_readable=True, theme='white')
# cf.getThemes()
nltk.download('stopwords')
nltk.download('punkt')
class PlotlyDashboard:
def __init__(self,
rows: int = 1,
cols: int = 1,
print_grid=False,
width=900,
height=500,
title='Dashboard 1',
params: dict = {}):
self.dashboard = None
self.rows = rows
self.cols = cols
self.print_grid = print_grid
self.width = width
self.height = height
self.title = title
self.params = params
self._create()
def _create(self):
self.dashboard = plotly.subplots.make_subplots(rows=self.rows,
cols=self.cols,
print_grid=self.print_grid,
**self.params)
self.dashboard['layout'].update(height=self.height,
width=self.width,
title_text=self.title)
def add_board(self,
trace: any,
row: int = 1,
col: int = 1,
layout_params: dict = {}):
self.dashboard.add_trace(trace=trace, row=row, col=col)
if layout_params != None:
for k, v in layout_params.items():
self.dashboard['layout'][k].update(**v)
return self
def show(self):
self.dashboard.show()
!ls -lha 'data/'
В качестве набора данных для журнала Scientific Reports представлен файл в формате '.csv' - scientific_reports_metadata.csv, размером 3.6 Гб
sr = pd.read_csv('data/scientific_reports_metadata.csv')
Обзор части данных
sr.head(3).iloc[0]
display(HTML('|| '.join([f'<b>{str(x)}</b>' for x in sr.columns])))
sr.shape
Набор данных содержит 105324 статей и 19 признаковых описаний
print("Количество полнстью дублирубщих значений: ",
len(sr) - len(sr.drop_duplicates()))
print("Количество дубликатов по заголовку: ",
len(sr) - len(sr.drop_duplicates(subset=['title'])))
Количечтво дублирующих заголовков 28925. Выглядит странно. Возможно журнал отнес одну и ту же статью к разным разделам, т.е. статья одновременно относиться к разным тематикам. Проверим это предположение.
# найдем дубликаты
duplicates = sr[sr.duplicated(subset=['title'])]
# возьмем один дубликат, найдем его в основном наборе данных,
# посмотрим на различие в остальных метаданных
display(sr[sr['title'] == duplicates.iloc[0]['title']])
del(duplicates)
Первый пример показал, что существует одна и та же статья, но в разных журналах, и время публикации тоже отличается в одном из журналов. Посчитаем статистически в скольких разных журналах в среднем повторяется каждая неуникальная статья.
duplicates = sr[sr.duplicated(subset=['title'])]
journal_name_stat = []
for dup_title in notebook.tqdm(list(duplicates['title'])):
journal_name_stat.append(len(sr[sr['title'] == dup_title]['journal_name']))
# Считаем средние значения
print('среднее значение повторяемости неуникальной статьи: ',
np.mean(journal_name_stat))
del (journal_name_stat, duplicates, dup_title)
В среднем статья повторияется в 2-х разных журналах. Так как среднее значение больше 2-x, значит бывает что и в 3-х. Интересная особенность журнала - публиковать в 2-х или 3-х разных тематических разделах одну и ту же статью.
print(
"Дубликаты по многим переменным: ",
len(sr) - len(
sr.drop_duplicates(subset=[
'journal_name', 'title', 'authors', 'abstract', 'affilations',
'aff_authors', 'subjects', 'biblio'
])))
По большинству основных переменных дубликатов нет.
Такие статьи скорее всего нужно оставлять, так как они по видимому относятся к нескольким разделам, что тоже важно при выявлении тематик статьи.
pd.DataFrame({"NaN": sr.isna().sum(), 'None': (sr == 'None').sum()})
Некоторые переменные имеют null-значения. Можно в будущем удалить статьи где в этих переменных нет данных.
sr_clearning_vars = [
'text', 'country_name', 'country_lat', 'country_lon', 'city_name',
'city_lat', 'city_lon'
]
Посмотрим на переменную обозначающую дату создания записи
sr.create_date[:10]
Переменная показывает дату скачивания статьи скриптом, и никак не связана с отальными данными статьи. Такую переменную можно убрать из дальнейшего анализа.
sr_dropping_vars = ['create_date']
sr_dropping_vars = list(set(sr_dropping_vars))
В переменной biblio, есть дата публикации статьи, посмотрим совпадает ли эта дата с датой из переменной publish_date
for col in ['publish_date', 'biblio']:
display(HTML(f"<b>{col}</b>"))
display(sr.iloc[0][col])
переменная biblio, содержит в себе значение переменной из publish date, поэтому наверное эта переменная может быть удалена из переменных для дальнейшего анализа.
sr_dropping_vars.append('publish_date')
sr_dropping_vars = list(set(sr_dropping_vars))
Переменна type содержит тип статьи. Определим, сколько типов статей есть в данном журнале
sr['type'].value_counts()
Журнал имеет 2 типа статей, второй тип Conference Proceeding, встречается всего 15 раз. Найдем эти 15 статей и посмотрим на их остальные данные.
sr.drop(sr_dropping_vars, axis=1)[sr.drop(sr_dropping_vars, axis=1)['type'] ==
'Conference Proceeding'][:3]
Отличий по типу статьи никаких не выявлено, можно удалить переменну, не удаляя стати принадлежащие к этому типу.
sr_dropping_vars.append('type')
sr_dropping_vars = list(set(sr_dropping_vars))
В описании написано, что из аффиляции были извлечены переменные: country_name, country_lat, country_lon, city_name, city_lat, city_lon
sr.drop(sr_dropping_vars, axis=1)[[
'affilations', 'country_name', 'country_lat', 'country_lon', 'city_name',
'city_lat', 'city_lon'
]][:3]
Выходит, что теперь можно удалить переменную affilations и пользоваться извлеченными переменными.
sr_dropping_vars.append('affilations')
sr_dropping_vars = list(set(sr_dropping_vars))
for col in ['authors', 'aff_authors', 'affilations']:
display(HTML(f"<b>{col}</b>"))
display(ast.literal_eval(sr.iloc[2][col]))
Для примера у нас есть 11 авторов. Они из 4 разных университетов или других организаций, у них есть 4 записи аффиляции. В переменной aff_authors содержится перечисление авторов по афиляции, т.е. какие из этих 11 принадлежат к какой из аффиляций. Выходит, что authors тоже можно удалить, так как она дублирует авторов из aff_authors в неупорядоченном по аффиляции виде. Но по этой колонке можно понять первого автора, который всегда является ведущим. Возможно выясниться интересная закономерность по первому увтору. Оставим эту переменную.
Переменные title, abstract, text - представляют собой основну информацию по содержимому статьи, их нужно оставить для анализа текстовой составлящей. Причем переменная text - это массив, где содаржиться текст статьи разделенный на подразделы. Какой именно это раздел не понятно, но понятно что это раздел.
sr.drop(sr_dropping_vars, axis=1)[['title', 'abstract', 'text']].head(3)
sr.drop(sr_dropping_vars, axis=1)[['journal_name', 'file_path']].head(3)
file_path - путь к статье на диске, не очень полезная переменная для анализа, journal_name - имя журнала в котором размещена статья. Начало имени у всех журналов одинаковое, название можно сократить.
sr_dropping_vars.append('file_path')
sr_dropping_vars = list(set(sr_dropping_vars))
.csv с именем scientific_reports_metadata.csv, имеет размер 3.6 Гб, а также 4 папками именнованными названиями журналов размером ... Представленный набор данных содержит 105324 - записи, и 19 переменных. В ходе процесса загрузки файлов журнала были определены следующие признаки, составляющие набор данных:
дата загрузки статьи [create_date] (время загрузки статьи с помощью скрипта)
название журнала [journal_name] (подраздел более общего журнала Scientific Reports)
дата публикации статьи в журнале [publish_date] (т.е. когда статья вошла в список статей журнала)
тип статьи [type] (тип данной статьи, т.е. такой какой она определяется на страничках журнала Nature)
заголовок статьи [title] (оригинальное название статьи), авторы [authors] (перечисление автора и соавторов)
аннотация [abstract] (аннотация статьи)
аффиляция [affilations] (аффиляция каждого из авторов статьи)
авторы по аффиляции [aff_authors] (перечисление авторов в порядке следования аффиляций)
тематика [subjects] (тематика или ключевые темы затронутые в статье помимо основной тематики журнала)
информация по публикации [biblio] (информация по ходу публикационной последовательности в журнале)
текст статьи [text] (массив с подразделами текста статьи)
путь к файлу [file_path] (путь к файлу .pdf статьи на диске)
названия стран [country_name] (название стран авторов по аффиляции)
значения широты страны [country_lat] (значение широты расположения страны на карте по аффиляции)
значение долготы [country_lon] (значения долготы расположения страны на карты по аффиляции)
названия городов [city_name] (название городов извлечённых из аффиляции авторов)
значения широты городов [city_lat] (значение широты расположения городов на карте по аффиляции)
значения долготы городов [city_lon] (значение долготы расположения городов на карте по аффиляции)
не содержит полностью дублирующих значений, но существует 28925 дубликатов статей по заголовкам. Дальнейший анализ показал, что некоторые статьи одновременно принадлежат двум и более тематикам, среднее значение повторяемости статьи 2.12 т.е. некоторые статьи дублируются для 2 или 3 тематик одновременно. небольшое количество Null значений (максимальное значение 258) в некоторых переменных (text, country_name, country_lat, country_lon, city_name, city_lat, city_lat). Объекты имеющие Null значения в этих переменных могут быть удалены.Анализ отдельных переменных набора показал следующие особенности:
Переменная create_date показывающая время скачивания статьи не связана с другими переменными, может быть использована как показатель среднего времени, затрачиваемого на скачивание статьи при анализе последовательности значений, но данном анализе не представляет большого интереса.
Значение переменной publish_date, дублируется в переменной biblio, как одно из значений, и может быть удалена из дальнейшего анализа.
Тип статьи, обозначенный переменной type имеет большой перекос в сторону одного типа, а именно 105309 значений типа Article и 15 значений типа Conference Proceeding. Остальные переменные не имеют никакой связи с данной переменной, что не дает никакого полезного результата для анализа и может быть удалена как вся переменная, не затрагивая выборки по объектам.
Переменные affilations, country_name (1), country_lat (2) country_lon (3), city_name (4), city_lat (5), city_lon (6) - имеют прямую взаимосвязь, т.е. все переменные 2-6 являются результатом извлечения данных из переменной 1. Таким образом переменная 1 может быть удалена.
Авторы (authors) и авторы по аффиляции (aff_authors) - похожи друг на друга, так как содержат перечисления авторов в шапке статьи и авторов, расставленных в порядке аффиляции. Но переменная authors может быть использована для анализа страны ведущего автора, поэтому должна быть оставлена в наборе данных.
Основные переменные title, abstract, text - основное содержание статьи обязательно должны присутствовать в наборе.
Ссылка на .pdf файл статьи file_path) в данном анализе не представляется важной и может быть удалена. Переменная journal_name - важная переменная, показывающая основное направление деятельности ученых публикующих свои работы.
# Удаление переменных с null-значениями
sr_clearning_vars = [
'text', 'country_name', 'country_lat', 'country_lon', 'city_name',
'city_lat', 'city_lon'
]
sr_tmp = sr.copy()
# Удаление значений с None
for col_for_clear in sr_clearning_vars:
sr_tmp = sr_tmp[sr_tmp[col_for_clear] != 'None']
# Удаление значений с NaN
sr_tmp.dropna(inplace=True)
# Удаление переменных
sr_prepared = sr_tmp.drop(sr_dropping_vars, axis=1)
# Сокращение имени названия журанала
sr_prepared['journal_name'] = sr_prepared['journal_name'].apply(
lambda x: x.split('__')[1])
del (col_for_clear, sr_tmp)
pd.DataFrame({
"NaN": sr_prepared.isna().sum(),
'None': (sr_prepared == 'None').sum()
})
Null-значений больше нет. Преобразование сделано верно
sr_prepared.shape
sr_prepared.head(3)
Предполагается, что каждая тема статьи значительно отличается по популярности от других тем. Построим распределение статей из набора данных по разным тематикам (журналам) из общего набора Scientific Reports и визуализируем результаты.
(PlotlyDashboard(
rows=1,
cols=1,
height=500,
width=900,
title='<b>Распределение статей по журналам</b>',
params=dict(
vertical_spacing=0.3,
horizontal_spacing=0.1,
)).add_board(go.Bar(
x=[x for x in list(sr_prepared['journal_name'].value_counts().index)],
y=list(sr_prepared['journal_name'].value_counts().values),
name='Deep Learning',
orientation='v',
),
row=1,
col=1,
layout_params=dict(xaxis1=dict(title_text="Журнал",
automargin=True),
yaxis1=dict(title_text="Статьи",
automargin=False)))).show()
Распределение статей по журналам показывает, что популярность разных тематик неодинакова. Тема биологии (biological_sciences) занимает лидирующее место среди 4 представленных тематик и имеет порядка 39000 статей. В порядке убывания по популярности расположились науки о здоровье (health_sciences), физические науки (physical_sciences) и науки о земле и окружающей среде (earth_and_environmental_sciences), с количеством статей порядка 33000, 22000 и 12000 соответственно.
Результаты предварительного анализа показали, что существуют статьи которые дублируются в двух разных журналах с разными тематиками в семействе Scientific Reropts. Посчитаем вероятоности попадания в один из дополнительных журналов при публикации статьи по одной из тематик.
# Имена журналов
j_names = list(sr_prepared.journal_name.unique())
# Заголовки дубликатов статей
journals_stats = {}
for i, j_name in enumerate(j_names): # Проход по журналам
duplicates = sr_prepared[sr_prepared.duplicated(subset=['title'])][[
'title', 'journal_name'
]]
# Ищем дубликаты и фильтруем по журналу
duplicates = duplicates[duplicates['journal_name'] == j_name]
journal_stats_tmp = {}
# Проходим по дубликатам журнала
for a_title, j_name in zip(duplicates['title'],
duplicates['journal_name']):
# Выбираем статьи которые дублируют эту, и откидываем
# дубликат к этому журналу
jn_list = list(sr_prepared[(sr_prepared['title'] == a_title)
& (sr_prepared['journal_name'] != j_name)]
['journal_name'])
for jn in jn_list:
# Пытаемся найти ключ журнала в словаре
if journal_stats_tmp.get(jn, 'Not_Found') == 'Not_Found':
journal_stats_tmp[jn] = 1
else: # Нашли ключ
# увеличиваем значение счетчика
journal_stats_tmp[jn] = (journal_stats_tmp[jn] + 1)
journals_stats[j_name] = {
k: v / np.sum(list(journal_stats_tmp.values())) * 100
for k, v in journal_stats_tmp.items()
}
pd.DataFrame(journals_stats).fillna(0).T.iplot(
kind='bar',
yTitle='Статьи, %',
xTitle='Журнал',
title='Частота публикации дубликатов в других журналах')
del (j_names, journals_stats, i, j_name, duplicates, journal_stats_tmp,
a_title, jn_list, jn)
статьи дублируются по заголовкам, что привело к выводу, что редакция журнала публикует дубликаты в нескольких разделах. Визуализация результатов определила, что вероятность печати дубликата статьи в другом журнале составляет: Для темы earth_and_environmental_sciences - 100% статей публикуются так же и в журнале biological_sciencesДля темы health_sciences - вероятность публикации дубликата составляет 25% для biological_sciences, 65% для earth_and_environmental_sciences, 9% для physical_sciencesДля темы physical_sciences - вероятность публикации дубликата составляет 18% для biological_sciences, 36% для earth_and_environmental_sciences, 44% для health_sciences Для темы biological_sciences - вероятность публикации составляет 18% для earth_and_environmental_sciences, 17% для physical_sciences, 64% для health_sciences Так как специфики работы над научными задачами различны для каждой из областей знаний, в среде ученых складываются определенные подходы к решению задач. Они могут заключаться в привлечении других ученых, которые вместе работают над публикациями. Предполагается, что для каждой из такой областей есть некоторое особенное среднее число ученых, которые работают совместно над публикациями. Посчитаем среднее количество ученых на статью по каждой из тематик и построим график.
sr_prepared_tmp = sr_prepared[['authors', 'journal_name']].copy()
sr_prepared_tmp['author_count'] = sr_prepared_tmp['authors'].apply(
lambda x: len(ast.literal_eval(x)))
# Имена журналов
j_names = list(sr_prepared.journal_name.unique())
journals_stats = {}
for j_name in j_names:
journals_stats[j_name] = sr_prepared_tmp[sr_prepared_tmp['journal_name'] ==
j_name]['author_count'].mean()
pd.Series(journals_stats).iplot(
kind='bar',
yTitle='Средне кол-во авторов',
xTitle='Журнал',
title='Среднее количество авторов распредленное по журналам')
del(sr_prepared_tmp, j_name, j_names)
Cтатистический подсчет результатов по каждой области показал отличия, которые нельзя считать существенными. Так в области earth_and_environmental_sciences в среднем над статьями работает 6.1 авторов, в области health_sciences среднее значение авторов составляет 8.6, в области physical_sciences - 6.4, а в области biological_sciences - 7.5.
Помимо основных тем журналов существует еще тематика каждой статьи, которая определяется переменной subject, она как бы конкретизирует то, что описывается в статье. Есть предположение что есть некоторое подмножество таких тематик, вокруг которого сосредоточены большинство публикаций. Построим визуализацию графики и отобразим процентное соотношение тематик, определённых в статьях.
subject_distrib = pd.Series(
Enumerable(sr_prepared['subjects'].apply(lambda x: ast.literal_eval(x))).
select_many(lambda x: x).to_list()).value_counts()
for j_count, j_title in zip([len(subject_distrib), 50], [
'Распределение статей по тематикам (все тематики)',
'Распределение статей по тематикам (первые 50)'
]):
(PlotlyDashboard(rows=1,
cols=1,
height=500,
width=900,
title=j_title,
params=dict(
vertical_spacing=0.3,
horizontal_spacing=0.1,
)).add_board(go.Bar(
x=subject_distrib.index[:j_count],
y=(subject_distrib.values /
np.sum(subject_distrib.values) * 100)[:j_count],
name='Deep Learning',
orientation='v',
),
row=1,
col=1,
layout_params=dict(
xaxis1=dict(title_text="Тематика",
automargin=True),
yaxis1=dict(
title_text="Использование в статьях, %",
automargin=False)))).show()
В результате можно заключить, что таких тематик не существует. Самая популярная Risk factors имеет всего 1.13% статей остальные тематики имеют вокруг себя меньше одного процента, что не позволяет сделать выводы о наличии доминирующих тематик.
# import plotly.graph_objects as go
# labels = ['Russia','China','USA','Nitrogen']
# values = [4500, 2500, 1053, 500]
# # Use `hole` to create a donut-like pie chart
# fig = go.Figure(data=[go.Pie(labels=labels, values=values, hole=.1)])
# fig.show()
pd.DataFrame({'one:' [23], 'two': [14]})
Повышенное финансирование науки влечет за собой увеличение числа научных работ по всему миру. Предполагается так же что время отправки статьи, время принятия в печать и время публикации наиболее часто приходится на какие-то определённые месяцы года и дни недели. Посчитаем статистики и построим графики для интерпретации результатов.
# сделаем копию набора данных для манипуляций
sr_prepared_tmp = sr_prepared.copy()
# Выделим в отдельные переменные дату получеия статьи, принятия в печать,
# и публикации в журнале
# (pos - позиция в массиве, name - название столбца который будет создан,
# length - длина для отрезания слова до даты)
action_type = ['received', 'accepted', 'published']
for pos, name, length in zip([0, 1, 2], action_type, [8, 8, 9]):
sr_prepared_tmp[name] = sr_prepared_tmp['biblio'].apply(
lambda x: ast.literal_eval(x)[pos][length:])
def _get_years_stat(df: pd.DataFrame, data_col: str):
"""Возвращает год отсортированные по убыванию"""
return df[data_col].apply(
lambda x: dateparser.parse(x).year).value_counts().sort_index()
def _get_month_stat(df: pd.DataFrame, data_col: str):
months = df[data_col].apply(
lambda x: dateparser.parse(x).month).value_counts().sort_index()
return pd.Series(data=months.values,
index=[calendar.month_name[x] for x in months.index])
def _get_weekday_stat(df: pd.DataFrame, data_col: str):
weekdays = df[data_col].apply(
lambda x: dateparser.parse(x).weekday()).value_counts().sort_index()
wd_name_arr = []
for wd in weekdays.index:
wd_name = {
i: weekday_name
for i, weekday_name in enumerate(list(calendar.day_name))
}.get(wd)
wd_name_arr.append(wd_name)
return pd.Series(data=weekdays.values, index=wd_name_arr)
for act_type, graph_title in zip(
action_type,
['отправки', 'принятия в печать', 'публикации в журнале']):
dashboard = PlotlyDashboard(
rows=1,
cols=3,
height=400,
width=900,
title=f'Распределение статей по времени {graph_title}',
params=dict(
subplot_titles=[
'Распределение по годам', 'Распределение по месяцам',
'Распределение по дням недели'
],
vertical_spacing=0.1,
horizontal_spacing=0.1,
))
for pos, func, xaxis, yaxis, xaxis_title in zip(
[1, 2, 3], [_get_years_stat, _get_month_stat, _get_weekday_stat],
['xaxis1', 'xaxis2', 'xaxis3'], ['yaxis1', 'yaxis2', 'yaxis3'],
['год', 'месяц', 'день']):
res = func(sr_prepared_tmp, act_type)
dashboard.add_board(go.Scatter(x=res.index,
y=res.values,
name='',
orientation='v',
mode='lines+markers',
line=dict(width=1),
marker=dict(line=dict(width=1),
size=10,
color='gray'),
showlegend=False),
row=1,
col=pos,
layout_params={
xaxis:
dict(title_text=xaxis_title, automargin=True),
yaxis:
dict(title_text="статьи", automargin=False)
})
dashboard.show()
del (sr_prepared_tmp, action_type, pos, name, length, act_type, graph_title,
dashboard, func, xaxis, yaxis, xaxis_title, res)
В результате получаем, что количество поданных статей в журнал постепенно росло до 2014 года. Постепенный рост возможно связан с тем, что журнал только набирал свою популярность, так как судя по периодам публикации первый прием начался в 2011 году. С 2015 года был резкий рост количества публикаций и с 2017 года началось падение. Точный результат за 2019 год еще не определен, так как исследование не учитывает полный 2019 год, но скорее всего тенденция падения может сохраниться. Также можно заметить, что наибольшая активность авторов при отправке статей приходиться на январь, наименьшая на сентябрь с резким ростом в октябре и практическим сходом на минимальное количество в преддверии новогодних праздников, т.е. в декабре. Для отправки статей авторы обычно используют будние дни.
Прием статей в печать обычно происходит в октябре и чаще всего в понедельник или среду. Менее всего редакция принимает работы в печать в начале года. Но в начале года количество принятых статей начинает расти до середины лета.
Публиковать статьи предпочитают обычно в середине лета и совсем мало в декабре в преддверии новогодних праздников. Распределение по дням недели так же равномерно начиная с понедельника по пятницу.
Принято считать, что с течением времени некоторые научные направления теряют свою привлекательность для ученых, а некоторые набирают. Построим графики изменения популярности определенных научных направлений в течении периода существования журнала.
# сделаем копию набора данных для манипуляций
sr_prepared_tmp = sr_prepared.copy()
# Выделим в отдельные переменные дату получеия статьи
sr_prepared_tmp['received'] = sr_prepared_tmp['biblio'].apply(
lambda x: ast.literal_eval(x)[0][8:])
# Имена журналов
j_names = list(sr_prepared.journal_name.unique())
def _get_years_stat(df: pd.DataFrame, data_col: str):
"""Возвращает год отсортированный по убыванию"""
return df[data_col].apply(
lambda x: dateparser.parse(x).year).value_counts().sort_index()
df = []
for j_name in j_names:
res = _get_years_stat(
sr_prepared_tmp[sr_prepared_tmp['journal_name'] == j_name],
'received')
df.append(pd.DataFrame({j_name: res.values}, index=res.index))
pd.concat(df,
axis=1).fillna(0).iplot(yTitle='Статьи',
xTitle='Год',
title='Распределение получения по журналам',
mode='lines+markers')
del (sr_prepared_tmp, j_names, df, j_name, res)
Измерение популярности определенной области науки в течении периода существования журнала показывает, что все области сначала набирали популярность потом начали ее терять. Определённой зависимости по научном направлению не видно, но прослеживается общая тенденция, как и в случае результата, полученного выше, после 2016 года начался общий спад популярности в науке (суждение по 1 журналу, нельзя считать истинным показателем).
Предположительно, что в разных областях науки сложность рецензирования меняется, что влечет за собой повышение времени рецензирования. Построим графики среднего времени рецензирования для каждой области наук в журнале с течением времени.
# сделаем копию набора данных для манипуляций
sr_prepared_tmp = sr_prepared.copy()
# Выделим в отдельные переменные дату получеия статьи, принятия в печать,
# и публикации в журнале
# (pos - позиция в массиве, name - название столбца который будет создан,
# length - длина для отрезания слова до даты)
action_type = ['received', 'accepted', 'published']
for pos, name, length in zip([0, 1, 2], action_type, [8, 8, 9]):
sr_prepared_tmp[name] = sr_prepared_tmp['biblio'].apply(
lambda x: ast.literal_eval(x)[pos][length:])
#tqdm.pandas(desc="my bar!")
# считаем сколько дней проходит от получения статьи редакцией до принятия в печать
sr_prepared_tmp['waiting_accepted_days'] = (
sr_prepared_tmp['accepted'].apply(lambda x: dateparser.parse(x)) -
sr_prepared_tmp['received'].apply(lambda x: dateparser.parse(x))
).apply(lambda x: x.days)
# Извлечение года публикации
sr_prepared_tmp['accepted_year'] = sr_prepared_tmp['accepted'].apply(
lambda x: int(dateparser.parse(x).year))
# Имена журналов
j_names = list(sr_prepared.journal_name.unique())
df = []
for j_name in j_names:
summary = sr_prepared_tmp[sr_prepared_tmp['journal_name'] == j_name][[
'accepted_year', 'waiting_accepted_days'
]].groupby('accepted_year').mean()
summary.index = [int(x) for x in summary.index]
summary.rename(columns={'waiting_accepted_days': j_name}, inplace=True)
df.append(summary)
pd.concat(df,
axis=1).fillna(0).iplot(yTitle='Дни',
xTitle='Год',
title='Среднее время рецензирования статьи',
mode='lines+markers')
del (sr_prepared_tmp, action_type, pos, name, length, j_names, df, j_name,
summary)
Результаты показывают, что с каждым годом практически равномерно растет время ожидания принятия статьи, кроме 2016 года, когда время немного сократилось. Если судить по результатам, полученным выше, то можно заменить что 2016 год был самым продуктивным на количество присланных работ. Отсюда вывод, что время рецензирования сократили из-за большого количества присланных статей. Но это только предположение. Равномерность времени по всем научным направлениям объясняется скорее всего политикой журнала, а не со сложностью рецензирования статей. Точно ответить на этот вопрос используя данный набор данных невозможно, так как необходима выборка учитывающая и другие журналы.
В разных странах на науку выделяются различное финансирование от чего зависит количество ученых, работающих в сфере науки и предоставляющие свои результаты. Построим распределение ученых по странам мира.
# Удалим статьи где из аффиляции не смогла извлечься страна
for graph_name, variant in zip(
['по абсалютным значениям', 'по относительным значениям'], [1, 2]):
if variant == 1:
result = pd.Series(
x for x in Enumerable(sr_prepared['country_name'].apply(
lambda x: x.split('###'))).select_many(lambda x: x).to_list()
if x != 'None').value_counts()[:25]
else:
result = pd.Series(
x for x in Enumerable(sr_prepared['country_name'].apply(
lambda x: np.unique(x.split('###')))).select_many(
lambda x: x).to_list() if x != 'None').value_counts()[:25]
result.iplot(
kind='bar',
yTitle='Статьи',
xTitle='Страна',
title=f'Распределение авторов по странам({graph_name})')
Исходя из первого графика, т.е. используя абсолютные значения китайцев больше всего учувствует в науке, но если посмотреть на относительные числа, то есть статьи, принадлежащие авторам то можно увидеть, что Китай перемещается на второй место и лидерами является США. Россия занимает примерно 18 строчку по количеству статей, опубликованных в этом журнале. По одному журналу тяжело делать вывод о публикационной активности стран. Вполне возможно, что Российские ученые предпочитают другие журналы для публикации своих трудов.
В каждой стране может наблюдаться приоритет в одну или нескольких областей наук, в которую больше всего вкладывается денежных средств. Эта область может лидировать с наибольшим количеством публикаций. Построим распределение для приоритетных областей наук по нескольким странам.
# копия набора, для манипуляций над данными
sr_prepared_tmp = sr_prepared.copy()
# страны принимающие участие в публикациях
sr_prepared_tmp['countries_participant'] = sr_prepared_tmp[
'country_name'].apply(lambda x: np.unique(x.split('###')))
# список направлений журналов
j_names = list(sr_prepared.journal_name.unique())
dfs = []
for j_name in j_names: # Проход по журналам
# считаем количество стран, которые используют технологии
res = pd.Series(
Enumerable(sr_prepared_tmp[sr_prepared_tmp['journal_name'] == j_name]
['countries_participant']).select_many(
lambda x: x).to_list()).value_counts()
dfs.append(pd.DataFrame({j_name: res.values}, index=res.index))
# выбираем наиболее интересные страны
pd.concat(dfs, axis=1, sort=False).fillna(0).loc[[
'China', 'United States of America',
'United Kingdom of Great Britain and Northern Ireland', 'Germany',
'France', 'Japan', 'Russian Federation', 'Italy', 'India', 'Sweden',
'Netherlands', 'Brazil', 'Korea, Republic of', 'South Africa', 'Czechia',
'Israel'
]].iplot(kind='bar',
yTitle='Статьи',
xTitle='Страна',
title="Распределение стран по приоритетным областям наук")
del (sr_prepared_tmp, j_names, dfs, j_name, res)
Большинство стран, своими приоритетными областями выбрали науки о здоровье и биологию. Россия отличается этими показателями и в приоритете у России находятся физические науки, что скорее говорит о приоритете сторону техники и технологий и меньшем приоритете в сторону здоровья и медицины. Хотя судить по одному журналу истинные целы будет некорректным.
Если построить распределение стран, занимающихся наукой на карте мира то будет видна общая глобальная тенденция публикационной активности по странам. Построим распределение публикаций по странам и визуализируем на карте.
lat = [
x for x in pd.Series(
Enumerable(sr_prepared['country_lat'].apply(lambda x: x.split('###'))).
select_many(lambda x: x).to_list()).values if x != 'None'
]
lon = [
x for x in pd.Series(
Enumerable(sr_prepared['country_lon'].apply(lambda x: x.split('###'))).
select_many(lambda x: x).to_list()).values if x != 'None'
]
latitude = pd.Series(lat).value_counts()
longitude = pd.Series(lon).value_counts()
_map = folium.Map(location=[26.537737, 17.275546],
tiles='cartodbpositron',
zoom_start=2,
zoom_control=False)
folium.plugins.ScrollZoomToggler().add_to(_map)
marker_cluster = MarkerCluster().add_to(_map)
for _lat, _lon in zip(latitude.index, longitude.index):
folium.Marker(location=[_lat, _lon],
icon=folium.Icon(icon=None, color='lightgray')).add_to(_map)
display(_map)
del (lat, lon, latitude, longitude, _map, marker_cluster, _lat, _lon)
По карте можно видеть, что практически во всех странах мира есть ученые публикующие результаты в данном журнале.
Судя по количеству публикаций по странам определённое в предыдущих разделах, скорее всего и плотность распределения публикаций по городам миру будет несопоставима между собой. Построим распределение публикаций по городам по странам: США, Китай, Индия и Россия.
for county_name, country_coord, zoom in zip([
'United States of America',
'China',
'India',
'Russian Federation',
], [[39.942689, -97.448506], [34.435388, 106.189923], [22.537717, 79.209629],
[63.020506, 93.065389]], [4, 4, 4, 3]):
display(HTML(f'<h3>{county_name}</h3>'))
cities_coord_arr = []
for countries, cities, lats, lons in zip(
sr_prepared['country_name'].apply(lambda x: x.split('###')),
sr_prepared['city_name'].apply(lambda x: x.split('###')),
sr_prepared['city_lat'].apply(lambda x: x.split('###')),
sr_prepared['city_lon'].apply(lambda x: x.split('###'))):
if county_name in countries: # Если хоть одна страна есть в списке
# Извлекаем индексы страны
country_indexes = [
i for i, x in enumerate(countries) if x == county_name
]
for country_index in country_indexes:
if lats[country_index] != 'None' and lons[
country_index] != 'None':
cities_coord_arr.append(
[lats[country_index], lons[country_index]])
_map = folium.Map(
location=country_coord,
# tiles='mapquestopen',
zoom_start=zoom,
zoom_control=False)
folium.TileLayer('Stamen Toner').add_to(_map)
folium.plugins.ScrollZoomToggler().add_to(_map)
folium.plugins.Fullscreen().add_to(_map)
marker_cluster = MarkerCluster().add_to(_map)
for loc in list(pd.Series(cities_coord_arr).value_counts().index):
folium.Marker(location=[loc[0], loc[1]],
icon=folium.Icon(icon='university',
prefix='fa',
color='blue')).add_to(_map)
display(_map)
del (country_coord, zoom, cities_coord_arr, county_name, countries,
country_indexes, country_index, _map, marker_cluster, loc)
Распределение во всех странах кроме России плотное, и можно сделать вывод, что практически все как минимум крупные города занимаются наукой. В России же наблюдается другая картина. Города, занимающиеся наукой распределены в основном на европейской части России и находятся ближе к южной части, в то время как северная часть практически никак не задействована.
В каждом городе или регионе есть более популярные научные направления и менее популярные научные направления. Построим распределение научных направлений по странам: США, Китай, Индия и Россия.
country_names_arr = [
'United States of America',
'China',
'India',
'Russian Federation',
]
for county_name, country_coord, zoom in zip(
country_names_arr, [[39.942689, -97.448506], [34.435388, 106.189923],
[22.537717, 79.209629], [63.020506, 93.065389]],
[4, 4, 4, 3]):
display(HTML(f'<h3>{county_name}</h3>'))
cities_coord_arr = [] # координаты городов
journal_names_arr = [] # названия журналов
for countries, cities, lats, lons, journal in zip(
sr_prepared['country_name'].apply(lambda x: x.split('###')),
sr_prepared['city_name'].apply(lambda x: x.split('###')),
sr_prepared['city_lat'].apply(lambda x: x.split('###')),
sr_prepared['city_lon'].apply(lambda x: x.split('###')),
sr_prepared['journal_name']):
if county_name in countries: # Если хоть одна страна есть в списке
# Извлекаем индексы страны
country_indexes = [
i for i, x in enumerate(countries) if x == county_name
]
for country_index in country_indexes:
# Проверяем чтобы не было пустых кординат,
# и имена городов не назывались как страны
if lats[country_index] != 'None' and lons[
country_index] != 'None' and cities[
country_index] not in country_names_arr + [
'Russia'
]:
cities_coord_arr.append(
[lats[country_index], lons[country_index]])
journal_names_arr.append(journal)
_map = folium.Map(
location=country_coord,
#tiles='mapquestopen',
zoom_start=zoom,
zoom_control=False)
folium.TileLayer('cartodbpositron').add_to(_map)
folium.plugins.ScrollZoomToggler().add_to(_map)
folium.plugins.Fullscreen().add_to(_map)
# Карта цветов для области
color_map = {
'biological_sciences': 'orange',
'physical_sciences': 'lightblue',
'health_sciences': 'purple',
'earth_and_environmental_sciences': 'darkgreen'
}
# соединяем координаты городов с их научными направлениями,
# считаем количество научных работ по направлению для города
science_result = pd.Series([
f'{str(k)}###{str(v)}'
for k, v in zip(journal_names_arr, cities_coord_arr)
]).value_counts()
# Переведем в проценты
science_result = science_result / science_result.sum() * 100
# Для того чтобы не было слишком много объектов на карте,
# отрежем 15% от конца самых незначительных
science_result = science_result[:len(science_result) -
int(len(science_result) * 0.15)]
# Проходим по названию и направлению, и размеру направления
for area_name_loc, size in zip(science_result.index,
science_result.values):
# Извлекаем координаты
coord_city = ast.literal_eval(area_name_loc.split('###')[1])
# Извлекаем область
area = area_name_loc.split('###')[0]
folium.CircleMarker(coord_city,
radius=size * 5,
color=color_map[area],
fill_color=color_map[area],
fill=True,
fill_opacity=0.7).add_to(_map)
display(_map)
del (country_names_arr, county_name, country_coord, zoom, cities_coord_arr,
journal_names_arr, countries, cities, lats, lons, journal,
country_indexes, country_index, _map, color_map, science_result,
area_name_loc, size, coord_city, area)
На картах видно, что в России наиболее активно выделяется Москва и Новосибирск, остальные города или регионы имеют низкую активность. В США видно равномерное распределение научных направлений по всей стране с некоторыми глобальными центрами с областями наук. В России выделяется как Москва так и Новосибирск с приоритетом в physical_sciences, зетем biological_sciences, health_sciences и earth_and_environmental_sciences.
Предположительно, самые популярные слова в тексте исключая стоп-слова могут отображать суть публикации. Построим статистические распределения популярных слов и биграмм в заголовках статей и полных текстах и сравним их на близость с ключевыми тематиками (subjects) из метаданных статей.
journal_names = list(sr_prepared['journal_name'].unique())
dashboard = PlotlyDashboard(rows=2,
cols=2,
height=800,
width=900,
title='Распределение ключевых слов по тематикам',
params=dict(vertical_spacing=0.53,
horizontal_spacing=0.15,
subplot_titles=journal_names))
for journal_name, row, col, xaxis, yaxis in zip(
journal_names, [1, 1, 2, 2], [1, 2, 1, 2],
['xaxis1', 'xaxis2', 'xaxis3', 'xaxis4'],
['yaxis1', 'yaxis2', 'yaxis3', 'yaxis4']):
journal_keys = pd.Series(
Enumerable(
sr_prepared[sr_prepared['journal_name'] == journal_name]
['subjects']).select(lambda x: ast.literal_eval(x)).select_many(
lambda x: x).to_list()).value_counts()[:20]
dashboard.add_board(go.Scatter(x=journal_keys.index,
y=journal_keys.values,
mode='lines+markers',
line=dict(width=0.5),
marker=dict(line=dict(width=0.1), size=7),
showlegend=False),
row=row,
col=col,
layout_params={
xaxis:
dict(title_text="Ключевые слова", automargin=True),
yaxis:
dict(title_text="Статьи", automargin=False)
})
dashboard.show()
На диаграмме можно увидеть, что ключевые слова (subjects) распределенные по популярности определенные для статей, соответствуют тематикам каждого из 4-х журналов. Посмотрим на распределение слов из заголовков статей, можно ли их сопоставить с ключевыми словами.
def _n_gram_value_counts(text_arr, ngram_range=(1, 1), stop_words='english'):
cv = CountVectorizer(ngram_range=ngram_range, stop_words=stop_words)
transformed = cv.fit_transform(text_arr)
return pd.Series(
data=list(np.array(transformed.sum(axis=0))[0]),
index=list(pd.Series(
cv.vocabulary_).sort_values().index)).sort_values(ascending=False)
# создание списка стоп-слов
stop_word_list = stopwords.words('english')
for article_variant, graph_title in zip(['title', 'full_text'],
['заголовков', 'полного текста']):
journal_names = list(sr_prepared['journal_name'].unique())
for n_gram, dashboard_title, v_space, xtitle in zip([(1, 1), (2, 2)],
['слов', 'биграмм'],
[0.3, 0.53],
['Слова', 'Биграммы']):
dashboard = PlotlyDashboard(
rows=2,
cols=2,
height=700,
width=900,
title=
f'<b>Распределение {dashboard_title} {graph_title} статей по тематикам</b>',
params=dict(vertical_spacing=v_space,
horizontal_spacing=0.15,
subplot_titles=journal_names))
for journal_name, row, col, xaxis, yaxis in zip(
journal_names, [1, 1, 2, 2], [1, 2, 1, 2],
['xaxis1', 'xaxis2', 'xaxis3', 'xaxis4'],
['yaxis1', 'yaxis2', 'yaxis3', 'yaxis4']):
if article_variant == 'title':
# Заголовки статей
text = sr_prepared[sr_prepared['journal_name'] ==
journal_name]['title']
else:
# Полный текст статей
text = Enumerable(
sr_prepared[sr_prepared['journal_name'] == journal_name]
['text'].apply(lambda x: ast.literal_eval(x))).select_many(
lambda x: x).to_list()
# Извлечение n-грамм и подсчет статистики встречаемости
journal_keys = _n_gram_value_counts(text, n_gram,
stop_word_list)[:20]
dashboard.add_board(go.Scatter(x=journal_keys.index,
y=journal_keys.values,
mode='lines+markers',
line=dict(width=0.5),
marker=dict(line=dict(width=0.1),
size=7),
showlegend=False),
row=row,
col=col,
layout_params={
xaxis:
dict(title_text=xtitle, automargin=True),
yaxis:
dict(title_text="Количество",
automargin=False)
})
dashboard.show()
На графиках видно, что популярные слова в тексте и в заголовках не отражают сути тематик, которые были обозначены как ключевые в переменной subjects.
Распределение статей по журналам показывает, что популярность разных тематик неодинакова. Тема биологии (biological_sciences) занимает лидирующее место среди 4 представленных тематик и имеет порядка 39000 статей. В порядке убывания по популярности расположились науки о здоровье (health_sciences), физические науки (physical_sciences) и науки о земле и окружающей среде (earth_and_environmental_sciences), с количеством статей порядка 33000, 22000 и 12000 соответственно.
Предварительный анализ набора данных показал, что статьи дублируются по заголовкам, что привело к выводу, что редакция журнала публикует дубликаты в нескольких разделах. Визуализация результатов определила, что вероятность печати дубликата статьи в другом журнале составляет:
Для темы earth_and_environmental_sciences - 100% статей публикуются так же и в журнале biological_sciencesДля темы health_sciences - вероятность публикации дубликата составляет 25% для biological_sciences, 65% для earth_and_environmental_sciences, 9% для physical_sciencesДля темы physical_sciences - вероятность публикации дубликата составляет 18% для biological_sciences, 36% для earth_and_environmental_sciences, 44% для health_sciences Для темы biological_sciences - вероятность публикации составляет 18% для earth_and_environmental_sciences, 17% для physical_sciences, 64% для health_sciences Предполагается что тематика каждого журнала по-своему обособлена. Среднее количество авторов, работающих над статьями, может так же быть некой особенностью определенной области знаний. Но статистический подсчет результатов по каждой области показал отличия, которые нельзя считать существенными. Так в области earth_and_environmental_sciences в среднем над статьями работает 6.1 авторов, в области health_sciences среднее значение авторов составляет 8.6, в области physical_sciences - 6.4, а в области biological_sciences - 7.5.
На этапе анализа возникало предположение, что существует некоторое количество тематик выраженные переменной subject, вокруг которых может находиться большое количество статей, считающиеся популярными, но результат показал, что таких тематик не существует. Самая популярная Risk factors имеет всего 1.13% статей остальные тематики имеют вокруг себя меньше одного процента, что не позволяет сделать выводы о наличии доминирующих тематик.
Количество поданных статей в журнал постепенно росло до 2014 года. С 2015 года был резкий рост количества публикаций и с 2017 года началось падение. Точный результат за 2019 год еще не определен, так как исследование не учитывает полный 2019 год, но скорее всего тенденция падения может сохраниться. Также можно заметить, что наибольшая активность авторов при отправке статей приходиться на январь, наименьшая на сентябрь с резким ростом в октябре и практическим сходом на минимальное количество в декабре. Для отправки статей авторы обычно используют будние дни. Прием статей в печать обычно происходит в октябре и чаще всего в понедельник или среду. Менее всего редакция принимает работы в печать в начале года. Но в начале года количество принятых статей начинает расти до середины лета. Публиковать статьи предпочитают обычно в середине лета и совсем мало в декабре. Распределение по дням недели так же равномерно начиная с понедельника по пятницу`.
Измерение популярности определенной области науки в течении периода существования журнала показывает, что все области сначала набирали популярность потом начали ее терять. Определённой зависимости по научном направлению не видно, но прослеживается общая тенденция, как и в случае результата, полученного выше, после 2016 года начался общий спад популярности в науке (суждение по 1 журналу, нельзя считать истинным показателем).
Статистика расчета времени ожидания статьи с рецензии говорит о том что это время растет с каждым годом практически равномерно, исклбчая 2016 год, когда время немного сократилось. Если судить по результатам, полученным выше, то можно заменить что 2016 год был самым продуктивным на количество присланных работ. Отсюда вывод, что время рецензирования сократили из-за большого количества присланных статей. Но это только предположение. Равномерность времени по всем научным направлениям объясняется скорее всего политикой журнала, а не со сложностью рецензирования статей. Точно ответить на этот вопрос используя данный набор данных невозможно, так как необходима выборка учитывающая и другие журналы.
Публикационная активность России в исследуемом журнале находиться на 18 месте, в то время как США и Китай занимают первое и второе место соотвественно.
Большинство стран, своими приоритетными областями выбрали науки о здоровье и биологию. Россия отличается этими показателями и в приоритете у России находятся физические науки, что скорее говорит о приоритете сторону техники и технологий и меньшем приоритете в сторону здоровья и медицины. Хотя судить по одному журналу истинные целы будет некорректным.
Визуализация на карте показала, что публикационная активность присутствует практически во всех странах мира. Распределение во всех странах кроме России плотное, и можно сделать вывод, что практически все как минимум крупные города занимаются наукой. В России же наблюдается другая картина. Города, занимающиеся наукой распределены в основном на европейской части России и находятся ближе к южной части, в то время как северная часть практически никак не задействована. Также на картах видно, что в России наиболее активно выделяется Москва и Новосибирск, остальные города или регионы имеют низкую активность. В США видно равномерное распределение научных направлений по всей стране с некоторыми глобальными центрами с областями наук. В России выделяется как Москва так и Новосибирск с приоритетом в physical_sciences, зетем biological_sciences, health_sciences и earth_and_environmental_sciences.
Визуализация самых частых слов в полных текстах и заголовках статей показала что они не отражает основную тематику которая была объвлена в переменной subjects.